from PyQt4.QtCore import QSettings
from PyQt4.QtCore import QString
from PyQt4.QtCore import QVariant
from PyQt4.QtCore import SIGNAL
from PyQt4.QtGui import QDialog
from PyQt4.QtGui import QFileDialog
from deploy.constants import Config
from deploy.gui.autoLayout import AutoLayoutMixin
import logging

logger = logging.getLogger(__name__)

class SettingsDialog(QDialog, AutoLayoutMixin):

    # For AutoLayoutMixin
    LAYOUT_FILE_NAME = 'settings.ui'

    def __init__(self, parent = None):
        super(SettingsDialog, self).__init__(parent)

        self._settings = {}

        self.loadUi()
        self._initializeSettings()
        self._connectSignals()

    def _initializeSettings(self):
        self._settings = {
            Config.ENVIRONMENT_CONFIG_FILE : FilePathSetting(Config.ENVIRONMENT_CONFIG_FILE,
                                                             self.environmentConfigFilePath),
            Config.VNC_TOOL_PATH : FilePathSetting(Config.VNC_TOOL_PATH,
                                                   self.vncToolPath),
            Config.TEXT_EDITOR_TOOL_PATH : FilePathSetting(Config.TEXT_EDITOR_TOOL_PATH,
                                                           self.textEditorToolPath),
            Config.LOG_VIEWER_TOOL_PATH : FilePathSetting(Config.LOG_VIEWER_TOOL_PATH,
                                                          self.logViewerToolPath),
            Config.SERVICE_POLLING_FREQUENCY : SpinBoxSetting(Config.SERVICE_POLLING_FREQUENCY,
                                                              self.servicePollingFrequencySpin),
            Config.DEBUG_LEVEL : ComboBoxSetting(Config.DEBUG_LEVEL,
                                                 self.debugLevelCombo),
        }

        for setting in self._settings.values():
            setting.load()

    def _connectSignals(self):
        self.connect(self, SIGNAL('accepted()'), self.saveSettings)

        # Connect environment config file button signal
        self.connect(self.selectEnvironmentConfigPathButton, SIGNAL('clicked()'), self._getEnvironmentConfigFile)

        # Connect tool path button signals
        self.connect(self.selectVncToolPathButton, SIGNAL('clicked()'), self._getFileForVncTool)
        self.connect(self.selectTextEditorToolPathButton, SIGNAL('clicked()'), self._getFileForTextEditorTool)
        self.connect(self.selectLogViewerToolPathButton, SIGNAL('clicked()'), self._getFileForLogViewerTool)

    def saveSettings(self):
        for setting in self._settings.values():
            if setting.changed:
                setting.save()

                # Emit a changed signal, so other consumers can respond to the change
                self.emit(SIGNAL('settingChanged(QString)'), setting.name)

    def _getEnvironmentConfigFile(self):
        fileName = self._selectFileWithDialog(caption = 'Select environment configuration file',
                                              filetypeFilter = '*.yaml')

        self._settings[Config.ENVIRONMENT_CONFIG_FILE].update(fileName)

    def _getFileForVncTool(self):
        fileName = self._selectExecutableWithDialog()
        self._settings[Config.VNC_TOOL_PATH].update(fileName)

    def _getFileForTextEditorTool(self):
        fileName = self._selectExecutableWithDialog()
        self._settings[Config.TEXT_EDITOR_TOOL_PATH].update(fileName)

    def _getFileForLogViewerTool(self):
        fileName = self._selectExecutableWithDialog()
        self._settings[Config.LOG_VIEWER_TOOL_PATH].update(fileName)

    def _selectExecutableWithDialog(self):
        return self._selectFileWithDialog(caption = 'Select executable',
                                          filetypeFilter = '*.exe')

    def _selectFileWithDialog(self,
                              caption = 'Select file',
                              initialDir = None,
                              filetypeFilter = None):
        '''Show a modal select file dialog to select a file.'''

        filename = QFileDialog.getOpenFileName(self,
                                               QString(caption),
                                               QString(initialDir) if initialDir else QString(),
                                               QString(filetypeFilter) if filetypeFilter else QString(''),

                                               # The remaining attributes are currently unused
                                               QString(), #selectedFilter
                                               QFileDialog.Options()) #options

        # Return a string (not a QString)
        return str(filename)

class BaseSetting(object):
    def __init__(self,
                 name,
                 control):

        if self.__class__ is BaseSetting:
            raise NotImplementedError('%s is abstract, and should not be instantiated directly.' % self.__class__.__name__)

        self.name = name
        self.control = control

    def _getStoredValue(self):
        raise NotImplementedError('%s must implement a "_getStoredValue" method.' % self.__class__.__name__)

    def _setStoredValue(self, value):
        settings = QSettings()
        settings.setValue(self.name, QVariant(value))

        # Ensure that the settings are written to disk
        settings.sync()

    def _getCurrentValue(self):
        raise NotImplementedError('%s must implement a "_getCurrentValue" method.' % self.__class__.__name__)

    def _setCurrentValue(self, value):
        raise NotImplementedError('%s must implement a "_setCurrentValue" method.' % self.__class__.__name__)

    def load(self):
        value = self._getStoredValue()
        self._setCurrentValue(value)

        logger.info('Loaded setting %s: %s' % (self.name, value))

    @property
    def changed(self):
        return self._getCurrentValue() != self._getStoredValue()

    def save(self):
        currentValue = self._getCurrentValue()
        self._setStoredValue(currentValue)

        logger.info('Saving setting %s: %s' % (self.name, currentValue))

class SpinBoxSetting(BaseSetting):
    def _getCurrentValue(self):
        return self.control.value()

    def _setCurrentValue(self, value):
        self.control.setValue(value)

    def _getStoredValue(self):
        settings = QSettings()
        value, ok = settings.value(self.name).toInt()
        if not ok:
            value = 0

        return value

class FilePathSetting(BaseSetting):
    def _getCurrentValue(self):
        return self.control.text()

    def _setCurrentValue(self, value):
        self.control.setText(value)

    def _getStoredValue(self):
        settings = QSettings()
        value = settings.value(self.name).toString()

        return value

    def update(self, filePath):
        if filePath:
            self.textField.setText(filePath)

class ComboBoxSetting(BaseSetting):
    def _getCurrentValue(self):
        return self.control.currentText()

    def _setCurrentValue(self, value):
        for index in range(self.control.count()):
            available = str(self.control.itemText(index))
            if available == value:
                break

        self.control.setCurrentIndex(index)

    def _getStoredValue(self):
        settings = QSettings()

        value = str(settings.value(self.name).toString())
        return value
